﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Xml.XPath;
using System.Windows.Forms;
using System.Xml.Schema;
using System.Globalization;

namespace Barbar.UrlGuard.BL.Concrete {
  internal class MessageCollectionProvider : IMessageCollectionProvider {
    private ILanguageSchemaProvider m_LanguageSchemaProvider;
    private const string XmlNamespace = "http://urlguard.codeplex.com/language.xsd";

    public MessageCollectionProvider(ILanguageSchemaProvider languageSchemaProvider) {
      if (languageSchemaProvider == null)
        throw new ArgumentNullException("languageSchemaProvider");

      m_LanguageSchemaProvider = languageSchemaProvider;
    }


    private MessageCollection GetMessageCollection(string path) {
      if (string.IsNullOrEmpty(path))
        throw new ArgumentNullException("path");

      XmlReaderSettings settings = new XmlReaderSettings();
      settings.ValidationType = ValidationType.Schema;
      settings.Schemas.Add(m_LanguageSchemaProvider.GetSchema());

      MessageCollection result = new MessageCollection();
      XPathDocument document = null;
      try {
        using (TextReader textReader = File.OpenText(path)) {
          using (XmlReader reader = XmlReader.Create(textReader, settings)) {
            document = new XPathDocument(reader);
          }
        }
      } catch (DirectoryNotFoundException directoryNotFoundException) {
        throw new LanguageFileException(string.Format(CultureInfo.InvariantCulture, "{0} directory not found. Please re-install UrlGuard.", Path.GetDirectoryName(path)), path, directoryNotFoundException);
      } catch (FileNotFoundException fileNotFoundException) {
        throw new LanguageFileException(string.Format(CultureInfo.InvariantCulture, "{0} file not found. Please re-install UrlGuard.", Path.GetFileName(path)), path, fileNotFoundException);
      } catch (UnauthorizedAccessException accessException) {
        throw new LanguageFileException(string.Format(CultureInfo.InvariantCulture, "{0} file is not accessible. Please close program that uses it.", Path.GetFileName(path)), path, accessException);
      } catch (XmlException xmlException) {
        throw new LanguageFileException(string.Format(CultureInfo.InvariantCulture, "{0} file is not a valid xml. Please re-install UrlGuard.", Path.GetFileName(path)), path, xmlException);
      } catch (XmlSchemaValidationException validationException) {
        throw new LanguageFileException(string.Format(CultureInfo.InvariantCulture, "{0} file malformed - validation failed. Please re-install UrlGuard.", Path.GetFileName(path)), path, validationException);
      }
      
      XPathNavigator navigator = document.CreateNavigator();
      XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
      manager.AddNamespace("l", XmlNamespace);

      result.LanguageCode = navigator.SelectSingleNode("/l:language/@code", manager).Value;
      foreach (XPathNavigator element in navigator.Select("/l:language/l:message", manager)) {
        Message message = new Message();
        message.Key = element.SelectSingleNode("@key").Value;
        message.Value = element.Value;
        result.Add(message);
      }

      return result;
    }

    private IEnumerable<string> GetMessageFiles() {
      string directory = Path.GetDirectoryName(Application.ExecutablePath);
      string messagesDirectory = Path.Combine(Path.Combine(directory, "Resources"), "Languages");
      string[] files = Directory.GetFiles(messagesDirectory, "*.xml");
      string mainFile = Path.Combine(messagesDirectory, "en-GB.xml");
      LanguageFileException exception = new LanguageFileException(string.Format(CultureInfo.InvariantCulture, "Main language file was not found - {0}. Please re-install UrlGuard.", mainFile), mainFile, null);
      if (files == null)
        throw exception;
      bool mainFileFound = false;
      foreach (string file in files) {
        if (string.Compare(Path.GetFileName(file), "en-GB.xml", StringComparison.OrdinalIgnoreCase) == 0) {
          mainFileFound = true;
          break;
        }
      }
      if (!mainFileFound)
        throw exception;
      return files;
    }

    #region IMessageCollectionProvider Members

    public IEnumerable<MessageCollection> GetMessageCollections() {
      foreach (string path in GetMessageFiles()) {
        yield return GetMessageCollection(path);
      }
    }

    #endregion
  }
}
